﻿//#define ShowCompanyInfo

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows;
using DemoMode.Views;
using System.ComponentModel;
using System.Windows.Input;
using System.Windows.Media;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Security;
using System.Xml.Linq;
using MessageBox = System.Windows.MessageBox;
using Microsoft.Win32;
using System.Reflection;

namespace DemoMode.ViewModels
{
    internal partial class MainWindowViewModel:INotifyPropertyChanged
    {
        public  string AuthorInfo { get; } = "版权所有 © 2023 - 2025 刘汉涛 心倾";
        public event PropertyChangedEventHandler PropertyChanged;
        public MainWindow ParentWindow { get;set; }
        public NotifyIcon NotifyIcon { get; set; }
        ToolStripMenuItem[] menuItems;
        readonly ContextMenuStrip contextMenuStrip = new ContextMenuStrip();
        private string _notifyText = "演示模式";
        public string NotifyText
        {
            get { return _notifyText; }
            set
            {
                _notifyText = value;
                NotifyIcon.Text=value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(NotifyText)));
            }
        }
        Timer timer;
        Timer workTimerAndCountdownTimer;
        private int _intervalTime = 60;
        public int IntervalTime
        {
            get { return _intervalTime; }
            set 
            {
                _intervalTime = value;
                CountDown = IntervalTime;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IntervalTime)));
            }
        }
        private bool _isNotInDemoMode = true;
        /// <summary>
        /// 是否【未处于】演示模式。（当初命名有点问题）
        /// </summary>
        public bool IsNotInDemoMode
        {
            get { return _isNotInDemoMode; }
            set
            {
                _isNotInDemoMode=value;
                NotifyIcon.ContextMenuStrip.Items[0].Enabled = value;
                NotifyIcon.ContextMenuStrip.Items[1].Enabled = !value;
                if(value)
                {
                    if (IsDefaultMode)
                    {
                        ParentWindow.WorkTimeText.Visibility=Visibility.Collapsed;
                    }
                    else
                    {
                        ParentWindow.CountDownText.Visibility = Visibility.Collapsed;
                    }
                    NotifyText = "未处于演示模式";
                    NotifyIcon.Icon = greenIcon;
                    BackgroundColor=inactiveColor;
                }
                else
                {
                    if (IsDefaultMode)
                    {
                        ParentWindow.WorkTimeText.Visibility = Visibility.Visible;
                    }
                    else
                    {
                        ParentWindow.CountDownText.Visibility = Visibility.Visible;
                    }
                    NotifyText = "正处于演示模式";
                    NotifyIcon.Icon = orangeIcon;
                    BackgroundColor = activeColor;
                }
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsNotInDemoMode)));
            }
        }
        private string _keyToPress = "LeftCtrl";
        private Key _key=Key.LeftCtrl;
        public Key Key
        {
            get=>_key;
            set
            {
                _key = value;
                KeyToPress = value.ToString();
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Key)));
            }
        }
        public string KeyToPress
        {
            get { return _keyToPress; }
            set
            {
                _keyToPress = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(KeyToPress)));
            }
        }
        private bool _isAutoEnterDemoMode=true;
        public bool IsAutoEnterDemoMode
        {
            get { return _isAutoEnterDemoMode; }
            set
            {
                _isAutoEnterDemoMode = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsAutoEnterDemoMode)));
            }
        }
        private int _countDown=7;
        private bool _isAutoRun = false;
        public bool IsAutoRun
        {
            get => _isAutoRun;
            set
            {
                try
                {
                    RegistryKey Reg;
                    Reg = Registry.CurrentUser.CreateSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run");
                    if (value)
                    {
                        Reg.SetValue("XinQing_DemoMode", System.Windows.Forms.Application.ExecutablePath);
                    }
                    else
                    {
                        Reg.DeleteValue("XinQing_DemoMode", false);
                    }
                    Reg.Close();
                }
                catch (Exception err)
                {
                    MessageBox.Show(err.Message, "错误", MessageBoxButton.OK, MessageBoxImage.Error);
                }
                finally
                {
                    _isAutoRun = value;
                    Settings.Default.IsAutoRun = value;
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsAutoRun)));
                }
            }
        }
        private bool _isWorkWhenSessionLock = false;
        /// <summary>
        /// 在锁屏时是否继续工作，仅适用于默认防锁屏模式。
        /// </summary>
        public bool IsWorkWhenSessionLock
        {
            get => _isWorkWhenSessionLock;
            set
            {
                _isWorkWhenSessionLock = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsWorkWhenSessionLock)));
            }
        }
        private int _keyPressCount = 0;
        private bool _isDefaultMode = true;
        /// <summary>
        /// 默认模式：阻止系统休眠；非默认模式：模拟按键。
        /// </summary>
        public bool IsDefaultMode
        {
            get => _isDefaultMode;
            set
            {
                _isDefaultMode = value;
                IsKeyPressMode = !value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsDefaultMode)));
            }
        }
        public bool IsKeyPressMode { get;set; }
        public int KeyPressCount
        {
            get => _keyPressCount;
            set
            {
                _keyPressCount=value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(KeyPressCount)));
            }
        }
        public int CountDown
        {
            get => _countDown;
            set
            {
                _countDown=value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CountDown)));
            }
        }
        private readonly static SolidColorBrush activeColor = new SolidColorBrush(System.Windows.Media.Color.FromRgb(196, 196, 60));
        private readonly static SolidColorBrush inactiveColor = new SolidColorBrush(System.Windows.Media.Color.FromRgb(255, 255, 255));
        private SolidColorBrush _backgroundColor = inactiveColor;
        public SolidColorBrush BackgroundColor
        {
            get { return _backgroundColor; }
            set
            {
                _backgroundColor = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(BackgroundColor)));
            }
        }

        readonly static Bitmap orangeImage = new Bitmap(System.Windows.Application.GetResourceStream(new Uri("pack://application:,,,/DemoMode;component/Resources/OrangeCircle.png")).Stream);
        readonly static Bitmap greenImage = new Bitmap(System.Windows.Application.GetResourceStream(new Uri("pack://application:,,,/DemoMode;component/Resources/GreenCircle.png")).Stream);
        readonly static Bitmap logo = new Bitmap(System.Windows.Application.GetResourceStream(new Uri("pack://application:,,,/DemoMode;component/Resources/Icon.ico")).Stream);
        readonly static Icon greenIcon = System.Drawing.Icon.FromHandle(greenImage.GetHicon());
        readonly static Icon orangeIcon = System.Drawing.Icon.FromHandle(orangeImage.GetHicon());
        private readonly ICommand enterDemoModeCommand = new EnterDemoModeICommand();
        public ICommand EnterDemoModeCommand { get => enterDemoModeCommand; }
        private class EnterDemoModeICommand : ICommand
        {
            event EventHandler ICommand.CanExecuteChanged
            {
                add
                {
                    CommandManager.RequerySuggested += value;
                }

                remove
                {
                    CommandManager.RequerySuggested -= value;
                }
            }

            bool ICommand.CanExecute(object parameter)
            {
                return MainWindow.ViewModel.IsNotInDemoMode;
            }

            void ICommand.Execute(object parameter)
            {
                MainWindow.ViewModel.EnterDemoMode();
            }
        }
        private readonly ICommand exitDemoModeCommand = new ExitDemoModeICommand();
        public ICommand ExitDemoModeCommand { get => exitDemoModeCommand; }
        private class ExitDemoModeICommand : ICommand
        {
            event EventHandler ICommand.CanExecuteChanged
            {
                add
                {
                    CommandManager.RequerySuggested += value;
                }

                remove
                {
                    CommandManager.RequerySuggested -= value;
                }
            }

            bool ICommand.CanExecute(object parameter)
            {
                return !MainWindow.ViewModel.IsNotInDemoMode;
            }

            void ICommand.Execute(object parameter)
            {
                MainWindow.ViewModel.ExitDemoMode();
            }
        }




        internal MainWindowViewModel(MainWindow mainWindow)
        {
            ParentWindow = mainWindow;
            IsAutoRun =Settings.Default.IsAutoRun;
            IntervalTime = Settings.Default.IntervalTime;
            KeyToPress = Settings.Default.KeyToPress;
            IsAutoEnterDemoMode = Settings.Default.IsAutoEnterDemoMode;
            IsDefaultMode = Settings.Default.Mode;
            IsWorkWhenSessionLock=Settings.Default.IsWorkWhenSessionLock;
            var keyConverter = new KeyConverter();
            Key = (Key)keyConverter.ConvertFrom(KeyToPress);
            Microsoft.Win32.SystemEvents.SessionSwitch += SystemEvents_SessionSwitch;
            Initialize();
        }

        private void SystemEvents_SessionSwitch(object sender, SessionSwitchEventArgs e)
        {
            if (!IsWorkWhenSessionLock && !IsNotInDemoMode)
            {
                switch (e.Reason)
                {
                    case SessionSwitchReason.SessionLock:
                        if(IsDefaultMode)
                        {
                            SystemSleep.RestoreForCurrentThread();
                        }
                        else
                        {
                            timer.Stop();
                        }
                        break;
                    case SessionSwitchReason.SessionUnlock:
                        if (IsDefaultMode)
                        {
                            SystemSleep.PreventForCurrentThread();
                        }
                        else
                        {
                            timer.Start();
                        }
                        break;
                }
            }
        }

        private void Initialize()
        {
            try
            {
                InitializeTray();
                workTimerAndCountdownTimer = new Timer
                {
                    Interval = 1000,
                };
                workTimerAndCountdownTimer.Tick += WorkTimerAndCountdownTimer_Tick;
                if (Settings.Default.IsAutoEnterDemoMode) { EnterDemoMode();ParentWindow.Hide(); }
            }
            catch (Exception err)
            {
                MessageBox.Show(err.Message, "错误", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private void WorkTimerAndCountdownTimer_Tick(object sender, EventArgs e)
        {
            if (IsDefaultMode)
            {
                CountDown++;
            }
            else
            {
                CountDown--;
            }
        }

        private void InitializeTray()
        {
            ToolStripMenuItem menuItem1 = new ToolStripMenuItem
            {
                Text="进入演示模式",
                Image = orangeImage,
                Enabled=IsNotInDemoMode,
            };
            ToolStripMenuItem menuItem2 = new ToolStripMenuItem
            {
                Text = "退出演示模式",
                Image = greenImage,
                Enabled = !IsNotInDemoMode,
            };
            ToolStripMenuItem menuItemExit = new ToolStripMenuItem("关闭软件", logo);
            menuItems = new ToolStripMenuItem[] { menuItem1, menuItem2, menuItemExit };
            foreach (var i in menuItems)
            {
                i.Click += MenuItem_Click;
            }
            contextMenuStrip.Items.AddRange(menuItems);

            NotifyIcon = new NotifyIcon
            {
                Text = _notifyText,
                Icon = greenIcon,
                ContextMenuStrip = contextMenuStrip,
                Visible = true,
            };
            NotifyIcon.MouseClick += NotifyIcon_MouseClick;
        }
        private void EnterDemoMode()
        {
            try
            {
                if (IsDefaultMode)
                {
                    CountDown = 0;
                    SystemSleep.PreventForCurrentThread();
                }
                else
                {
                    if (timer != null) { timer.Stop(); timer.Dispose(); timer = null; }
                    timer = new Timer
                    {
                        Interval = IntervalTime * 1000
                    };
                    timer.Tick += Timer_Tick;
                    CountDown = IntervalTime;
                    KeyPressCount = 0;
                    timer.Start();
                    
                }
                workTimerAndCountdownTimer.Start();
                IsNotInDemoMode = false;
            }
            catch (Exception err)
            {
                MessageBox.Show(err.Message, "错误", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private void Timer_Tick(object sender, EventArgs e)
        {
            try
            {
                workTimerAndCountdownTimer.Stop();
                KeyboardToolkit.Keyboard.Type(Key);
                CountDown = IntervalTime;
                workTimerAndCountdownTimer.Start();
                KeyPressCount++;
            }
            catch//(Exception err)
            {
                //在锁屏时无法模拟按键。
                //MessageBox.Show(err.Message,"错误",MessageBoxButton.OK,MessageBoxImage.Error);
            }
        }
        private void ExitDemoMode()
        {
            try
            {
                if (IsDefaultMode)
                {
                    SystemSleep.RestoreForCurrentThread();
                }
                else
                {
                    if (timer != null)
                    {
                        timer.Stop();
                        timer.Tick -= Timer_Tick;
                        timer.Dispose();
                        timer = null;
                    }
                }
                workTimerAndCountdownTimer.Stop();
                IsNotInDemoMode = true;
            }
            catch (Exception err)
            {
                MessageBox.Show(err.Message, "错误", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
        private void NotifyIcon_MouseClick(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                if (ParentWindow.Visibility == Visibility.Hidden)
                {
                    ParentWindow.Show();
                    ParentWindow.Activate();
                }
                else
                {
                    ParentWindow.Hide();
                }
            }
        }

        private void MenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                switch ((sender as ToolStripMenuItem).Text)
                {
                    case "进入演示模式":
                        EnterDemoModeCommand.Execute(null);
                        break;
                    case "退出演示模式":
                        ExitDemoModeCommand.Execute(null);
                        break;
                    case "关闭软件":
                        ParentWindow.Close();
                        break;
                }
            }
            catch (Exception err)
            {
                MessageBox.Show(err.Message, "错误", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        static class KeyboardToolkit
        {
            internal static class NativeMethods
            {
                #region User32
                // Various Win32 constants
                internal const int KeyeventfKeyup = 0x0002;
                internal const int KeyeventfScancode = 0x0008;
                internal const int InputKeyboard = 1;

                // Various Win32 data structures
                [StructLayout(LayoutKind.Sequential)]
                internal struct INPUT
                {
                    internal int type;
                    internal INPUTUNION union;
                };

                [StructLayout(LayoutKind.Explicit)]
                internal struct INPUTUNION
                {
                    [FieldOffset(0)]
                    internal MOUSEINPUT mouseInput;
                    [FieldOffset(0)]
                    internal KEYBDINPUT keyboardInput;
                };

                [StructLayout(LayoutKind.Sequential)]
                internal struct MOUSEINPUT
                {
                    internal int dx;
                    internal int dy;
                    internal int mouseData;
                    internal int dwFlags;
                    internal int time;
                    internal IntPtr dwExtraInfo;
                };

                [StructLayout(LayoutKind.Sequential)]
                internal struct KEYBDINPUT
                {
                    internal short wVk;
                    internal short wScan;
                    internal int dwFlags;
                    internal int time;
                    internal IntPtr dwExtraInfo;
                };

                // Importing various Win32 APIs that we need for input
                [DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
                internal static extern int GetSystemMetrics(int nIndex);

                [DllImport("user32.dll", CharSet = CharSet.Auto)]
                internal static extern int MapVirtualKey(int nVirtKey, int nMapType);

                [DllImport("user32.dll", SetLastError = true)]
                internal static extern int SendInput(int nInputs, ref INPUT mi, int cbSize);

                [DllImport("user32.dll", CharSet = CharSet.Auto)]
                internal static extern short VkKeyScan(char ch);

                #endregion User32
            }

            public static class Keyboard
            {
                /// <summary>
                /// Presses down a key.
                /// </summary>
                /// <param name="key">The key to press.</param>
                public static void Press(Key key)
                {
                    SendKeyboardInput(key, true);
                }

                /// <summary>
                /// Releases a key.
                /// </summary>
                /// <param name="key">The key to release.</param>
                public static void Release(Key key)
                {
                    SendKeyboardInput(key, false);
                }

                /// <summary>
                /// Resets the system keyboard to a clean state.
                /// </summary>
                public static void Reset()
                {
                    foreach (Key key in Enum.GetValues(typeof(Key)))
                    {
                        if (key != Key.None && (System.Windows.Input.Keyboard.GetKeyStates(key) & KeyStates.Down) > 0)
                        {
                            Release(key);
                        }
                    }
                }

                /// <summary>
                /// Performs a press-and-release operation for the specified key, which is effectively equivallent to typing.
                /// </summary>
                /// <param name="key">The key to press.</param>
                public static void Type(Key key)
                {
                    Press(key);
                    //System.Threading.Thread.Sleep(100);
                    Release(key);
                }



                [PermissionSet(SecurityAction.Assert, Name = "FullTrust")]
                private static void SendKeyboardInput(Key key, bool press)
                {
                    PermissionSet permissions = new PermissionSet(PermissionState.Unrestricted);
                    permissions.Demand();

                    NativeMethods.INPUT ki = new NativeMethods.INPUT();
                    ki.type = NativeMethods.InputKeyboard;
                    ki.union.keyboardInput.wVk = (short)KeyInterop.VirtualKeyFromKey(key);
                    ki.union.keyboardInput.wScan = (short)NativeMethods.MapVirtualKey(ki.union.keyboardInput.wVk, 0);

                    int dwFlags = 0;

                    if (ki.union.keyboardInput.wScan > 0)
                    {
                        dwFlags |= NativeMethods.KeyeventfScancode;
                    }

                    if (!press)
                    {
                        dwFlags |= NativeMethods.KeyeventfKeyup;
                    }

                    ki.union.keyboardInput.dwFlags = dwFlags;

                    ki.union.keyboardInput.time = 0;
                    ki.union.keyboardInput.dwExtraInfo = new IntPtr(0);

                    if (NativeMethods.SendInput(1, ref ki, Marshal.SizeOf(ki)) == 0)
                    {
                        throw new Win32Exception(Marshal.GetLastWin32Error());
                    }
                }
            }
        }

        /// <summary>
        /// 包含控制屏幕关闭以及系统休眠相关的方法。
        /// </summary>
        public static class SystemSleep
        {
            [DllImport("kernel32")]
            private static extern ExecutionState SetThreadExecutionState(ExecutionState esFlags);
            [Flags]
            private enum ExecutionState : uint
            {
                /// <summary>
                /// Forces the system to be in the working state by resetting the system idle timer.
                /// </summary>
                SystemRequired = 0x01,

                /// <summary>
                /// Forces the display to be on by resetting the display idle timer.
                /// </summary>
                DisplayRequired = 0x02,

                /// <summary>
                /// This value is not supported. If <see cref="UserPresent"/> is combined with other esFlags values, the call will fail and none of the specified states will be set.
                /// </summary>
                [Obsolete("该值不受支持")]
                UserPresent = 0x04,

                /// <summary>
                /// Enables away mode. This value must be specified with <see cref="Continuous"/>.
                /// <para />
                /// Away mode should be used only by media-recording and media-distribution applications that must perform critical background processing on desktop computers while the computer appears to be sleeping.
                /// </summary>
                AwaymodeRequired = 0x40,

                /// <summary>
                /// Informs the system that the state being set should remain in effect until the next call that uses <see cref="Continuous"/> and one of the other state flags is cleared.
                /// </summary>
                Continuous = 0x80000000,
            }
            /// <summary>
            /// 设置此线程此时开始一直将处于运行状态，此时计算机不应该进入睡眠状态。
            /// 此线程退出后，设置将失效。
            /// 如果需要恢复，请调用 <see cref="RestoreForCurrentThread"/> 方法。
            /// </summary>
            /// <param name="keepDisplayOn">
            /// 表示是否应该同时保持屏幕不关闭。
            /// 对于游戏、视频和演示相关的任务需要保持屏幕不关闭；而对于后台服务、下载和监控等任务则不需要。
            /// </param>
            public static void PreventForCurrentThread(bool keepDisplayOn = true)
            {
                SetThreadExecutionState(keepDisplayOn
                    ? ExecutionState.Continuous | ExecutionState.SystemRequired | ExecutionState.DisplayRequired
                    : ExecutionState.Continuous | ExecutionState.SystemRequired);
            }

            /// <summary>
            /// 恢复此线程的运行状态，操作系统现在可以正常进入睡眠状态和关闭屏幕。
            /// </summary>
            public static void RestoreForCurrentThread()
            {
                SetThreadExecutionState(ExecutionState.Continuous);
            }

            /// <summary>
            /// 重置系统睡眠或者关闭屏幕的计时器，这样系统睡眠或者屏幕能够继续持续工作设定的超时时间。
            /// </summary>
            /// <param name="keepDisplayOn">
            /// 表示是否应该同时保持屏幕不关闭。
            /// 对于游戏、视频和演示相关的任务需要保持屏幕不关闭；而对于后台服务、下载和监控等任务则不需要。
            /// </param>
            public static void ResetIdle(bool keepDisplayOn = true)
            {
                SetThreadExecutionState(keepDisplayOn
                    ? ExecutionState.SystemRequired | ExecutionState.DisplayRequired
                    : ExecutionState.SystemRequired);
            }
        }
    }
}
